package app.PN;

import java.util.ArrayList;
import java.io.FileWriter;
import java.io.IOException;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class PetriNet {
	
	
	private int[][] pre;
	private int[][] post;
	private int[] marking;
	private int[] initialMarking;
	private ArrayList<String> transitions;
	private ArrayList<String> places;
	
	public PetriNet(ArrayList<String> nPlaces, ArrayList<String> trans){
		
		transitions = trans;
		places = nPlaces;
		pre = new int[nPlaces.size()][trans.size()];
		post = new int[nPlaces.size()][trans.size()];
		marking = new int[nPlaces.size()];
		initialMarking = new int[nPlaces.size()];
		for (int i = 0; i< places.size();i++){
			for (int j = 0; j<transitions.size();j++){
				pre[i][j]=0;
				post[i][j]=0;
			}
			marking[i]=0;
		}
		
		return;
		
	}
	
	public PetriNet() {
		// TODO Auto-generated constructor stub
	}
	public void debug_info(){
		System.out.println("Taille places:");
		System.out.println(places.size());
		System.out.println("Taille transitions:");
		System.out.println(transitions.size());
		String auxpre = "";
		String auxpost = "";
		for (int i =0;i< places.size();i++){
			for (int j=0;j<transitions.size();j++){
				auxpre +=" "+ pre[i][j];
				auxpost +=" "+ post[i][j];		
			}
			auxpre +="\n";
			auxpost +="\n";	
		}
		System.out.println("PRE:");
		System.out.println(auxpre);
		System.out.println("POST:");
		System.out.println(auxpost);
	}
	
	
	public boolean fromPNML(String path){
		
		try {

			
			final ArrayList<String> placesB = new ArrayList<String>();
			final ArrayList<String> transitionsB = new ArrayList<String>();
			final ArrayList<Integer>  iMarkingB = new ArrayList<Integer>();
			final ArrayList<String> arcsBfrom = new ArrayList<String>();
			final ArrayList<String> arcsBto = new ArrayList<String>();
			
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();
			
			
			DefaultHandler phandler = new DefaultHandler(){
				
				String temp;
				int auxmarking;
			    public void characters(char[] buffer, int start, int length) {
		              temp = new String(buffer, start, length);
		       }
		      

		       /*
		        * Every time the parser encounters the beginning of a new element,
		        * it calls this method, which resets the string buffer
		        */ 
		       public void startElement(String uri, String localName,
		                     String qName, Attributes attributes) throws SAXException {
		    	   	   
		              temp = "";

		              if (qName.equalsIgnoreCase("place")){
		            	  auxmarking = 0;
		            	  placesB.add(attributes.getValue("id"));
		            	  
		              }
		              
		              else if (qName.equalsIgnoreCase("transition")){
		            	  transitionsB.add(attributes.getValue("id"));
		              }
		              else if (qName.equalsIgnoreCase("arc")){
		            	  arcsBfrom.add(attributes.getValue("source"));
		            	  arcsBto.add(attributes.getValue("target"));
		              }
		       }

		       /*
		        * When the parser encounters the end of an element, it calls this method
		        */
		       public void endElement(String uri, String localName, String qName)
		                     throws SAXException {
		    	   
		    	   if (qName.equalsIgnoreCase("text")){
		    		   auxmarking = Integer.parseInt(temp);
		    	   }
		    	   else if (qName.equalsIgnoreCase("place")){
		    		   iMarkingB.add(auxmarking);
		    	   }
		       }
				
			};
			saxParser.parse(path,phandler);
			
			System.out.println("Size of lists:" + placesB.size() + " "+ transitionsB.size());
			
			int[][] preAux = new int[placesB.size()][transitionsB.size()];
			int[][] postAux = new int[placesB.size()][transitionsB.size()];
			
			System.out.println("places:"+placesB.toString());
			System.out.println("transitions:"+transitionsB.toString());
			for (int i=0;i<placesB.size();i++){
				for (int j =0;j<transitionsB.size();j++){
					preAux[i][j]=0;
					postAux[i][j]=0;
				}
			}
			System.out.println("0");
			System.out.println("N of arcs:" + arcsBto.size());
			if (arcsBfrom.size()!= arcsBto.size()){
			return false;
			}
			System.out.println("1");
			for (int i = 0;i <arcsBfrom.size();i++){
				System.out.println("Arc from :"+arcsBfrom.get(i)+ " to :"+arcsBto.get(i));
				
				if (placesB.contains(arcsBfrom.get(i))){
					if (transitionsB.contains(arcsBto.get(i))){
						preAux[placesB.indexOf(arcsBfrom.get(i))][transitionsB.indexOf(arcsBto.get(i))] = 1;
					}
					else{
						System.out.println("Aquin1");
						return false;
					}
				}
				else if(transitionsB.contains(arcsBfrom.get(i))){
					if (placesB.contains(arcsBto.get(i))){
						postAux[placesB.indexOf(arcsBto.get(i))][transitionsB.indexOf(arcsBfrom.get(i))] = 1;
					}
					else{
						System.out.println("Aquin2");
						return false;
					}
				}
				else{
					System.out.println("Aquin3");
					return false;
				}
			}
			System.out.println("Ok???");
		// Assignment of extracted data to the internal data structures
		
			this.transitions = transitionsB;
			this.places = placesB;
			
			this.initialMarking = new int[placesB.size()];
			for (int i=0;i<placesB.size();i++){
				this.initialMarking[i] = iMarkingB.get(i);
			}
			this.post = postAux;
			this.pre = preAux;
			return true;
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			return false;
		}

	}

	public boolean toPNML(String path){

		String aux = "";
		
		aux += "<pnml xmlns=\"http://www.example.org/pnml\">\n";
		aux += "<net id=\"n1\" type=\"http://www.examples.org/pnml/PTNet\">\n";
		aux += "<page id=\"top-level\">\n";
		for (int i=0;i<places.size();i++){
			aux +="<place id=\"" + places.get(i)+"\">\n";
			if (initialMarking[i]!=0){
				aux+="<initialMarking>\n";
				aux+="<text>"+initialMarking[i]+"</text>\n";
				aux+="</initialMarking>\n";
				}
			aux+="</place>\n";
		}
		for (int i=0;i<transitions.size();i++){
			aux +="<transition id=\"" + transitions.get(i)+"\">\n";
			aux+="</transition>\n";
		}
		int auxNumerotation=0;
		for (int i=0;i<transitions.size();i++){
			for (int j=0;j<places.size();j++){
				if (pre[j][i]!=0){
					aux+="<arc id=\"a" + auxNumerotation++ +"\" source=\""+places.get(j)+"\" target=\""+transitions.get(i)+"\">\n";
					aux+="<inscription>\n";
					aux+="<text>"+pre[j][i]+"</text>\n";
					aux+="</inscription>\n";
					aux+="</arc>\n";
				}
				if (post[j][i]!=0){
					aux+="<arc id=\"a" + auxNumerotation++ +"\" source=\""+transitions.get(i)+"\" target=\""+places.get(j)+"\">\n";
					aux+="<inscription>\n";
					aux+="<text>"+post[j][i]+"</text>\n";
					aux+="</inscription>\n";
					aux+="</arc>\n";
				}
			}
		}
		aux +="</page>\n";
		aux +="</net>\n";
		aux +="</pnml>\n";
		
		
		FileWriter fr;
		try {
			fr = new FileWriter(path);
			fr.write(aux);
			fr.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("ERROR");
			return false;
		}
		System.out.println(aux);
		return true;
	}
	
	
	public PetriNet clone(){
		PetriNet aux = new PetriNet(places,transitions);
		ArrayList<Integer> iM = new ArrayList<Integer>();
		for (int i = 0; i< marking.length;i++){
			iM.add(marking[i]);
		}
		
		for (int i = 0; i< places.size();i++){
			for (int j = 0; j<transitions.size();j++){
				aux.setIncomingEdgeTransition(transitions.get(j),places.get(i),pre[i][j]);
				aux.setOutcomingEdgeTransition(transitions.get(j),places.get(i),post[i][j]);
			}
		}
		aux.setInitialMarking(iM);
		return aux;
	}
	
	public boolean setInitialMarking(ArrayList<Integer> initialM){
		if(initialM.size() != marking.length){
			return false;
		}
		else{
			for (int i=0;i<marking.length;i++){
				marking[i] = initialM.get(i);
				initialMarking[i] = initialM.get(i);
			}
			return true;
		}
	}
	
	public boolean setIncomingEdgeTransition(String transition,String place,int weight){
		if (!places.contains(place) || !transitions.contains(transition)){
			return false;
		}
		else{
			pre[places.indexOf(place)][transitions.indexOf(transition)] = weight;
			return true;
		}
	}

	public boolean setOutcomingEdgeTransition(String transition,String place,int weight){
		if (!places.contains(place) || !transitions.contains(transition)){
			return false;
		}
		else{
			post[places.indexOf(place)][transitions.indexOf(transition)] = weight;
			return true;
		}
	}
	
	public boolean isFireable(String targetTransition){
		if (transitions.contains(targetTransition)){
			int[] aux = new int[places.size()];
			for (int i=0;i<places.size();i++){
				aux[i] =pre[i][transitions.indexOf(targetTransition)];	
			}
			boolean fireable = true;
			for(int i=0;i<aux.length;i++){
				fireable &= !(aux[i]>marking[i]);				
			}
			return fireable;
		}
		else{
			return false;
		}
		
	}
	
	public boolean fireTransition(String targetTransition){
		if (isFireable(targetTransition)){
			for (int i=0;i<places.size();i++){
				marking[i]= marking[i] - pre[i][transitions.indexOf(targetTransition)] + post[i][transitions.indexOf(targetTransition)];
			}
			return true;			
		}
		else 
			return false;
	}
	
	public void printMarking(){
		String aux = "";
		
		for (int i = 0;i<marking.length;i++){
			aux += marking[i] + " ";
		}
		
		System.out.println(aux);
		return;
	}

	public void reset(){
		marking = initialMarking.clone();
	}

	public ArrayList<String> getFireableTransitions() {
		// TODO Auto-generated method stub
		
		ArrayList<String> sol = new ArrayList<String>();
		
		for (int i = 0; i<transitions.size();i++){
			if (isFireable(transitions.get(i))){
				sol.add(transitions.get(i));
			}
		}
		return sol;
	}
	
}
